package applet;

import java.io.File;
import java.io.FileNotFoundException;
import java.util.Scanner;
import applet.forces.Force;
import parts.mass.FixedMass;
import parts.mass.Mass;
import parts.spring.Bar;
import parts.spring.Muscle;
import parts.spring.Spring;

/**
 * Takes an input file and creates masses or springs based
 * on what the file says.

 * @author Robert C. Duvall
 * edited by Jesse Starr and Guy Tracy
 */
public class Factory {
    private static final int maxAssemblyMasses = 1000;
    private double myAssemblyNum = 1.0;

    /**
     * In order to avoid the simulation
     * confusing the masses from the old assemblies and the
     * masses from the newly added assemblies, we added an
     * assembly number (similar to a version number) for each
     * set of masses/springs created. The maxAssemblyMasses simply
     * helps keep track of each mass within each "version"
     * (needs to be a high number so the assembly number doesn't
     * roll over). So each mass's ID is now a double, e.g. 1.0, 2.5, etc.
     * that is based on with what/when it was created.
     *
     * @param sim the Simulation to use
     * @param modelFile the file to use
     * @param aNum keeps track of the assembly number
     */
    public void loadModel (Simulation sim, File modelFile, double aNum) {
        try {
            Scanner input = new Scanner(modelFile);
            myAssemblyNum += aNum;
            while (input.hasNext()) {
                Scanner line = new Scanner(input.nextLine());
                if (line.hasNext()) {
                    String type = line.next();
                    if ("mass".equals(type)) {
                        sim.add(massCommand(line));
                    }
                    else if ("spring".equals(type)) {
                        sim.add(springCommand(line, sim));
                    }
                    else if ("muscle".equals(type)) {
                        sim.add(muscleCommand(line, sim));
                    }
                    else if ("gravity".equals(type)) {
                        gravityCommand(line, sim);
                    }
                    else if ("viscosity".equals(type)) {
                        viscosityCommand(line, sim);
                    }
                    else if ("centermass".equals(type)) {
                        centermassCommand(line, sim);
                    }
                    else if ("wall".equals(type)) {
                        wallCommand(line, sim);
                    }
                }
            }
            input.close();
        }
        catch (FileNotFoundException e) {
            // should not happen because File came from user selection
            e.printStackTrace();
        }
    }

    private Mass massCommand (Scanner line) {
        double id = myAssemblyNum + line.nextDouble() / maxAssemblyMasses;
        double x = line.nextDouble();
        double y = line.nextDouble();
        double mass = line.nextDouble();

        if (mass < 0) {
            return new FixedMass(id, x, y, mass);
        }
        return new Mass(id, x, y, mass);
    }

    private void gravityCommand(Scanner line, Simulation sim) {
        double direction = line.nextDouble();
        double magnitude = line.nextDouble();

        sim.getGlobal().setGDirection(direction);
        sim.getGlobal().setGMagnitude(magnitude);
        sim.getGlobal().toggleG();
    }

    private void viscosityCommand(Scanner line, Simulation sim) {
        double magnitude = line.nextDouble();

        sim.getGlobal().setVMagnitude(magnitude);
        sim.getGlobal().toggleV();
    }

    private void centermassCommand(Scanner line, Simulation sim) {
        double magnitude = line.nextDouble();
        double exponent = line.nextDouble();

        sim.getGlobal().setCOMMagnitude(magnitude);
        sim.getGlobal().setCOMExponent(exponent);
        sim.getGlobal().toggleC();
    }

    private void wallCommand(Scanner line, Simulation sim) {
        int id = line.nextInt();
        double magnitude = line.nextDouble();
        double exponent = line.nextDouble();

        sim.getGlobal().addWallData(magnitude, exponent, id);
    }

    private Spring springCommand (Scanner line, Simulation sim) {
        double m1 = myAssemblyNum + line.nextDouble() / maxAssemblyMasses;
        double m2 = myAssemblyNum + line.nextDouble() / maxAssemblyMasses;
        double restLength = line.nextDouble();
        double ks = line.nextDouble();

        if (ks < 0) {
            double dx = sim.getMass(m1).getCenter().getX()
                    - sim.getMass(m2).getCenter().getX();
            double dy = sim.getMass(m1).getCenter().getY()
                    - sim.getMass(m2).getCenter().getY();
            double barLength = Math.sqrt(dx * dx + dy * dy);

            return new Bar(sim.getMass(m1), sim.getMass(m2), barLength);
        }

        return new Spring(sim.getMass(m1), sim.getMass(m2), restLength, ks);
    }

    private Spring muscleCommand (Scanner line, Simulation sim) {
        double m1 = myAssemblyNum + line.nextDouble() / maxAssemblyMasses;
        double m2 = myAssemblyNum + line.nextDouble() / maxAssemblyMasses;
        double restLength = line.nextDouble();
        double ks = line.nextDouble();
        double amp = line.nextDouble();

        return new Muscle(sim.getMass(m1), sim.getMass(m2),
                restLength, ks, amp);
    }
}
